//this file is part of eMule
//Copyright (C)2002 Merkur ( merkur-@users.sourceforge.net / http://www.emule-project.net )
//
//This program is free software; you can redistribute it and/or
//modify it under the terms of the GNU General Public License
//as published by the Free Software Foundation; either
//version 2 of the License, or (at your option) any later version.
//
//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.
//
//You should have received a copy of the GNU General Public License
//along with this program; if not, write to the Free Software
//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

//#include "stdafx.h"
#include "wintypes.h"
#include "emule.h"
#include "SafeFile.h"
#include "ClientCredits.h"
#include "opcodes.h"
#include <math.h>
#include "/usr/include/time.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

CClientCredits::CClientCredits(CreditStruct* in_credits){
	m_pCredits = in_credits;
}

CClientCredits::CClientCredits(uchar* key){
	m_pCredits = new CreditStruct;
	memset(m_pCredits,0,sizeof(CreditStruct));
	memcpy(m_pCredits->abyKey,key,16);
}

CClientCredits::~CClientCredits(){
	delete m_pCredits;
}

float CClientCredits::GetScoreRatio()
{
  if(!m_pCredits) {
    return 1;
  }

	if (m_pCredits->nDownloaded < 1000000)
		return 1;
	float result = 0;
	if (!m_pCredits->nUploaded)
		result = 10;
	else
		result = (float)(m_pCredits->nDownloaded*2)/m_pCredits->nUploaded;
	float result2 = 0;
	result2 = (float)(m_pCredits->nDownloaded/1048576);
	result2 += 2;
	result2 = (double)sqrt((double)result2);

	if (result > result2)
		result = result2;

	if (result < 1) {
		return 1;
	}
	else if (result > 10) {
		return 10;
	}

	return result;
}


CClientCreditsList::CClientCreditsList(CPreferences* in_prefs)
{
	m_pAppPrefs = in_prefs;
	m_nLastSaved = ::GetTickCount();
	LoadList();
}

CClientCreditsList::~CClientCreditsList()
{
	SaveList();
	CClientCredits* cur_credit;
	CCKey tmpkey(0);
	POSITION pos = m_mapClients.GetStartPosition();
	while (pos){
		m_mapClients.GetNextAssoc(pos, tmpkey, cur_credit);
		delete cur_credit;
	}
	m_mapClients.RemoveAll();
}

void CClientCreditsList::LoadList()
{
	CSafeFile file;
	/*try*/{
		wxString strFileName = m_pAppPrefs->GetAppDir() + wxString("clients.met");
		if (!file.Open(strFileName, CFile::read)){
			theApp.emuledlg->AddLogLine(true, GetResString(IDS_ERR_LOADCREDITFILE));
			return;
		}
		uint8 version;
		file.Read(&version, 1);
		if (version != CREDITFILE_VERSION){
			theApp.emuledlg->AddLogLine(false, GetResString(IDS_ERR_CREDITFILEOLD));
			file.Close();
			return;
		}

		// everything is ok, lets see if the backup exist...
		CString strBakFileName;
		strBakFileName.Format("%sclients.met.BAK", m_pAppPrefs->GetAppDir());
		
		DWORD dwBakFileSize = 0;
		BOOL bCreateBackup = TRUE;

		//HANDLE hBakFile = ::CreateFile(strBakFileName, GENERIC_READ, FILE_SHARE_READ, NULL,
		int hBakFile=open(strBakFileName.GetData(),O_RDONLY);
		//OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

		if (hBakFile != (-1))
		{
			// Ok, the backup exist, get the size
			//dwBakFileSize = ::GetFileSize(hBakFile, NULL); //debug
		  struct stat sbuf;
		  fstat(hBakFile,&sbuf);
			if (sbuf.st_size > (DWORD)file.Length())
			{
				// the size of the backup was larger then the org. file, something is wrong here, don't overwrite old backup..
				bCreateBackup = FALSE;
			}
			//else: backup is smaller or the same size as org. file, proceed with copying of file
			//::CloseHandle(hBakFile);
			close(hBakFile);
		}
		//else: the backup doesn't exist, create it

		if (bCreateBackup)
		{
			file.Close(); // close the file before copying
			//BOOL bResult = ::CopyFile(strFileName, strBakFileName, FALSE);
			// safe? you bet it is
			char cmdbuffer[512];
			sprintf(cmdbuffer,"cp \"%s\" \"%s\"",strFileName.GetData(),strBakFileName.GetData());
			system(cmdbuffer);
			//DWORD dwError;
			//if (!bResult)
			//{
			//	dwError = ::GetLastError(); // debug
			//	theApp.emuledlg->AddLogLine(false, GetResString(IDS_ERR_MAKEBAKCREDITFILE));
			//}
			// reopen file
			if (!file.Open(strFileName, CFile::read))
			{
				theApp.emuledlg->AddLogLine(true, GetResString(IDS_ERR_LOADCREDITFILE));
				return;
			}
			//file.Seek(1);//, CFile::begin); //set filepointer
			file.Seek(1);
		}


		uint32 count;
		file.Read(&count, 4);
		m_mapClients.InitHashTable(count+5000); // TODO: should be prime number... and 20% larger

		for (uint32 i = 0; i != count; i++){
			CreditStruct* newcstruct = new CreditStruct;
			memset(newcstruct, 0, sizeof(CreditStruct));
			file.Read(newcstruct, sizeof(CreditStruct));
			CClientCredits* newcredits = new CClientCredits(newcstruct);
			m_mapClients.SetAt(CCKey(newcredits->GetKey()), newcredits);
			if(!(i%165)) {
			  // do not hog the UI
			  theApp.Yield();
			}
		}
		file.Close();
		theApp.emuledlg->AddLogLine(false, GetResString(IDS_CREDITFILELOADED), count);
	}
#if 0
	catch(CFileException* error){
		OUTPUT_DEBUG_TRACE();
		if (error->m_cause == CFileException::endOfFile)
			theApp.emuledlg->AddLogLine(true, GetResString(IDS_CREDITFILECORRUPT));
		else{
			char buffer[150];
			error->GetErrorMessage(buffer, 150);
			theApp.emuledlg->AddLogLine(true, GetResString(IDS_ERR_CREDITFILEREAD), buffer);
		}
		error->Delete();
	}
#endif
}

void CClientCreditsList::SaveList()
{
	uint32 count = m_mapClients.GetCount();
	BYTE* pBuffer = new BYTE[count*sizeof(CreditStruct)];
	wxString name = m_pAppPrefs->GetAppDir() + wxString("clients.met");
	CFile file;

	if (!file.Create(name,TRUE)) //Open(name, CFile::modeWrite|CFile::modeCreate))
	{
		theApp.emuledlg->AddLogLine(true, GetResString(IDS_ERR_FAILED_CREDITSAVE));
		return;
	}

	CClientCredits* cur_credit;
	CCKey tempkey(0);
	POSITION pos = m_mapClients.GetStartPosition();
	count = 0;
	while (pos)
	{
		m_mapClients.GetNextAssoc(pos, tempkey, cur_credit);
		if (cur_credit->GetDataStruct()->nUploaded + cur_credit->GetDataStruct()->nDownloaded > 0)
		{
			memcpy(pBuffer+(count*sizeof(CreditStruct)), cur_credit->GetDataStruct(), sizeof(CreditStruct));
			count++; 
		}
	}

	uint8 version = CREDITFILE_VERSION;
	file.Write(&version, 1);
	file.Write(&count, 4);
	file.Write(pBuffer, count*sizeof(CreditStruct));
	file.Close();

	delete[] pBuffer;
	m_nLastSaved = ::GetTickCount();
}

CClientCredits* CClientCreditsList::GetCredit(uchar* key)
{
	CClientCredits* result;
	CCKey tkey(key);
	if (!m_mapClients.Lookup(tkey, result)){
		result = new CClientCredits(key);
		m_mapClients.SetAt(CCKey(result->GetKey()), result);
	}
	result->SetLastSeen();
	return result;
}

void CClientCreditsList::Process()
{
	if (::GetTickCount() - m_nLastSaved > 70000)
		SaveList();
}



#if 0
float CClientCredits::GetScoreRatio(){
	if (credits->downloaded < 1000000)
		return 1;
	float result = 0;
	if (!credits->uploaded)
		result = 10;
	else
		result = (float)(credits->downloaded*2)/credits->uploaded;
	float result2 = 0;
	result2 = (float)(credits->downloaded/1048576);
	result2 += 2;
	result2 = (double)sqrt((double)result2);

	if (result > result2)
		result = result2;

	if (result < 1)
		return 1;
	else if (result > 10)
		return 10;
	return result;
}


CClientCreditsList::CClientCreditsList(CPreferences* in_prefs){
	app_prefs = in_prefs;
	lastsaved = ::GetTickCount();
	LoadList();
}

CClientCreditsList::~CClientCreditsList(){
	SaveList();
	CClientCredits* cur_credit;
	CCKey tmpkey(0);
	POSITION pos = client_map.GetStartPosition();
	while (pos){
		client_map.GetNextAssoc(pos,tmpkey,cur_credit);
		delete cur_credit;
	}
	client_map.RemoveAll();
}

void CClientCreditsList::LoadList(){
	CSafeFile file;
	{
		wxString name = app_prefs->GetAppDir() + wxString("clients.met");
		if (!file.Open(name,CFile::read)){
			theApp.emuledlg->AddLogLine(false,GetResString(IDS_ERR_LOADCREDITFILE));
			return;
		}
		uint8 version;
		file.Read(&version,1);
		if (version != CREDITFILE_VERSION){
			theApp.emuledlg->AddLogLine(false,GetResString(IDS_ERR_CREDITFILEOLD));
			file.Close();
			return;
		}
		uint32 count;
		file.Read(&count,4);
		client_map.InitHashTable(count+5000);
		for (uint32 i = 0; i != count;i++){
			Credit_Struct* newcstruct = new Credit_Struct;
			memset(newcstruct,0,sizeof(Credit_Struct));
			file.Read(newcstruct,sizeof(Credit_Struct));
			CClientCredits* newcredits = new CClientCredits(newcstruct);
			client_map.SetAt(CCKey(newcredits->GetKey()),(CClientCredits*)newcredits);
		}
		file.Close();
		theApp.emuledlg->AddLogLine(false,GetResString(IDS_CREDITFILELOADED),count);
	}
#if 0
	catch(CFileException* error){
		OUTPUT_DEBUG_TRACE();
		if (error->m_cause == CFileException::endOfFile)
			theApp.emuledlg->AddLogLine(true,GetResString(IDS_CREDITFILECORRUPT));
		else{
			char buffer[150];
			error->GetErrorMessage(buffer,150);
			theApp.emuledlg->AddLogLine(true,GetResString(IDS_ERR_CREDITFILEREAD),buffer);
		}
		error->Delete();
	}
#endif
}

void CClientCreditsList::SaveList(){
	wxString name = app_prefs->GetAppDir() + wxString("clients.met");
	CFile diskfile;
	CMemFile file(0, 0, 16384); 
	//if (!diskfile.Open(name,CFile::modeWrite|CFile::modeCreate)){
	if(!diskfile.Create(name,TRUE)) {
		theApp.emuledlg->AddLogLine(true,GetResString(IDS_ERR_FAILED_CREDITSAVE));
		return;
	}
	uint8 version = CREDITFILE_VERSION;
	file.Write(&version,1);
	uint32 count = client_map.GetCount();
	file.Write(&count,4);
	CClientCredits* cur_credit;
	CCKey tempkey(0);
	POSITION pos = client_map.GetStartPosition();
	count = 0;
	while (pos){
		client_map.GetNextAssoc(pos,tempkey,cur_credit);
		if (cur_credit->GetDataStruct()->uploaded + cur_credit->GetDataStruct()->downloaded > 0){ 
          file.Write(cur_credit->GetDataStruct(),sizeof(Credit_Struct)); 
          count++; 
		}
	}
	file.Seek(1);//,FILE_BEGIN); 
    file.Write(&count,4); 
    int size = file.GetLength(); 
    BYTE* tmp = file.Detach(); 
    diskfile.Write(tmp,size); 
    free(tmp); 
    diskfile.Close(); 
	lastsaved = ::GetTickCount();
}

CClientCredits* CClientCreditsList::GetCredit(uchar* key){
	CClientCredits* result;
	CCKey tkey(key);
	if (!client_map.Lookup(tkey,result)){
		result = new CClientCredits(key);
		client_map.SetAt(CCKey(result->GetKey()),result);
	}
	result->SetLastSeen();
	return result;
}

void CClientCreditsList::Process(){
	if (::GetTickCount() - lastsaved > 70000)
		SaveList();
}


#endif
